//: Playground - noun: a place where people can play

import UIKit

// 函数的几种类型

// 1、无参无反

func greet() -> Void {
    
}

// 或者

func greet1() {
    
}

// 2、有参无反

func greet2(_ person:String, on day: String) {
    
}

greet2("Bob", on: "Tuesday")

// 3、有参数有返回
func greet(_ person: String, on day: String) -> String {
    
    return "Hello \\\\(person), today is \\\\(day)."
}

// 4、无参数有返回

func greet() -> String {
    
    return "Hello."
}

// 参数与返回值

// 1、单值

func calculate(a:Int) {
    
    let b = a
    
    print(b)
}

// 2.多值

func caclulate1(a:Int...) {
    
    for b in a {
        
        print(b)
    }
}

caclulate1(a: 1,2,3,4,5)

// 3.元组
func calculate2(a:(name:String,age:Int)){
    
    let name = a.name;
    let age = a.age;
    
    print("\(name) \(age)");
}

calculate2(a: ("666", 777))

// 4.数组

func calculate3(a:[String]){
    
    for student in a {
        print(student)
    }
}

calculate3(a: ["222","3333","555"])

// 5.定义字典

func calculate4(a:[String:Int]){
    for student in a {
        print(student.key)
        print(student.value)
    }
}

calculate4(a: ["keykey":7777,"popo":9999])

// 6.函数作为参数
func add(a:Int, b:Int) -> Int {
    
    return a+b;
}

func calculate6(a:(Int, Int) -> Int) {
    
    a(2,1)
}

calculate6(a: add);

// 7.上面函数的闭包写法
calculate6 { (a,b) -> Int in return a+b; }

calculate6 { (a,b) in a+b}

// 8.参数为协议的方法

protocol Player {
    
    func play()
}

func playMusicWithPlayer(player:Player) {
    
    player.play()
}

class Player11: Player {

    func play() {
        
        print("ke yide xiao huo zi ")
    }
}

var player12 = Player11();

playMusicWithPlayer(player: player12)

// 9.参数为结构体
struct Student1 {
    
    var name:String
    var age:Int
}

func getStudentDescription(student:Student1) {
    
    print(student.name)
    print(student.age)
}

getStudentDescription(student: Student1(name: "343434", age: 232323))

// 10.参数为枚举类型
enum CarType:String {

    case Lincoin = "林肯"

    case MERCURY = "帅醒"
    
    case SUZUKI = "铃木"
}

func describeCar(carType:CarType) {

    print(carType.rawValue)
}

describeCar(carType: CarType.Lincoin)

// 函数的内部定义函数

func generateFuncByFlag(flag:Bool) -> (Int, Int) -> Int {

    func add(a: Int, b: Int) -> Int {
        
        return a + b
    }
    
    func decrease(a: Int, b: Int) -> Int {
    
        return a - b
    }
    
    if flag {
        
        return add
    } else {
    
        return decrease
    }
}

let addFunc = generateFuncByFlag(flag: false);

print(addFunc(1,2))

// 设置默认参数值  


func addStudent(student:(name:String, score:Double) = ("姓名", 12)) {

    print(student.name)
    print(student.1)
}

addStudent()
addStudent(student: ("枯燥",99.0))

/* 元祖类型，不能分别给参数赋值 eg：
func addStudent(student:(name:String = "酷走天涯",score:Double = 12 )){
    print(student.name)
    print(student.1)
}
 */

// inout的使用 声明函数时，在参数前面用inout修饰，函数内部实现改变外部参数

/*
 1.inout的位置 在: 后面,数据类型前面
 2.inout 修饰的参数不能有默认值
 3.inout 不能用于修饰多值(如a:Int...)
 4.一个参数一旦被inout修饰，就不能再被var和let修饰了。
 */


// 创建一个函数，交换两个Int类型的值
func swapTwoInts( a: inout Int, b: inout Int) {

    let temp = a
    a = b
    b = temp
}

var a = 30
var b = 40

swap(&a, &b)

print(a)
print(b)

// 定义函数类型的变量
var swap1:(inout Int, inout Int) -> Void = swapTwoInts

// 错误的写法  不能使用a,b标签
//var swap2:( a :inout Int, b: inout Int)->Void = swapTwoInts
// 你应该像下面这样
var swap3:( _ :inout Int, _: inout Int)->Void = swapTwoInts
// 或者下面这样也可以,a,b 不一定要和实际函数对应
var swap4:( _ a:inout Int, _ b: inout Int)->Void = swapTwoInts
// 建议还是用下面这种
var swap5:( inout Int, inout Int)->Void = swapTwoInts


// 定义闭包类型数据

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "daniella"]
let customerProvider = { customersInLine.remove(at: 0)}

print(customersInLine.count)
print(customerProvider())
print(customersInLine.count)

// 这种闭包其实是无参数有返回的形式，原型如下
let customerProvider1:() -> String = {

    customersInLine.remove(at: 0);
}

// 关键字 @discardableResult

/*
Swift 3.0 中方法的返回值必须有接收否则会报警告，当然其实主要目的是为了避免开发人员忘记接收返回值的情况，但是有些情况下确实不需要使用返回值可以使用"_"接收来忽略返回值。当然你也可以增加@discardableResult声明，告诉编译器此方法可以不用接收返回值。
 */

class OSStudent {
    
    @discardableResult
    func backBool() -> Bool {
        
        return true
    }
}

OSStudent().backBool()

// 获取函数自己名称，在那个文件中，在文件多少行

func getFunctionName(name:String = #function, line: Int = #line, file:String = #file) {

    print(name)
    print(line)
    print(file)
}

func getUserName() {

    getFunctionName()
}

getUserName()

class Person {

}

class Man: Person {

}

class Woman: Person {

}

// 定义三个描述人的方法
func describePerson(_ person:Person){
    print("我是人类")
}

func describePerson(_ woman:Woman){
    print("我是女人")
}

func describePerson(_ person:Man){
    print("我是男人")
}

// 定义一个描述男人的女人的方法
func descripePerson(_ person:Person,_ woman:Woman){
    describePerson(person)
    describePerson(woman)
}
// 执行
descripePerson(Man(), Woman())

func descripePerson1(_ person:Person, _ woman: Woman) {

    if person is Woman {
        
        describePerson(person as! Woman)
    } else {
    
        describePerson(person as! Man)
    }
    
    if let woman = person as? Woman {
        
        describePerson(woman)
    } else {
    
        describePerson(person as! Man)
    }
    
    describePerson(woman)
}

// 泛型

// 设计一个接口，交换两个元素（数字，字符，对象）的值
// 交换的必须是相同的对象

func swap<T>(a:inout T, b: inout T) {

    (a,b) = (b,a)
}

var a1 = "你好"
var b1 = "帅帅"
swap(&a1, &b1)
print(a1+b1)


// @escaping 作用 逃逸闭包
// 逃逸的闭包常用于异步的操作，这类函数会在异步操作开始之后立刻返回，但是闭包直到异步操作结束后才会被调用。例如这个闭包是异步处理一个网络请求，只有当请求结束后，闭包的生命周期才结束。当闭包作为函数的参数传入时，很有可能这个闭包在函数返回之后才会被执行。

var downloadComplate:((Bool)->Void)!

func downloadResource(url:String, complate:@escaping (Bool) -> Void) {

    downloadComplate = complate
    
    // 异步下载，下载完成调动
    
    downloadComplate(true)
    
    downloadComplate(false)
}

downloadResource(url: "www.baidu.com") { (flag) in
    
    print(flag)
}

// @autoclosure 的用法
/*
 (1)默认非逃逸
 (2)闭包也可以被自动的生成，这种闭包被称为自动闭包，自动闭包自动将表达式封装成闭包。
 (3)自动闭包不接收任何参数，被调用时会返回被包装在其中的表达式的值。
 (4)当闭包作为函数参数时，可以将参数标记 @autoclosure 来接收自动闭包。
 (5)自动闭包能够延迟求值,因为代码段不会被执行直到你调用这个闭包。
 (6)自动闭包默认是非逃逸的，如果要使用逃逸的闭包，需要手动声明: @autoclosure @escaping 旧版本：@autoclosure(escaping)
 */

// a.自动闭包演示
var students1 = ["A","B","C"];
let studentsProvider = { students1.remove(at: 0)}// 自动闭包自动将表达式封装成闭包
studentsProvider()

// b.自动闭包演示
var list = [1,2,3,4,5,6]

// 创建一个显示闭包
let closures = { list.append(7)}

print(list)
closures()
print(list)

func func1(closure: () -> Void) -> Void {
    
    // 执行显示的闭包
    closure()
}

func func2(auto: @autoclosure () -> Void) -> Void {
    
    // 执行自动闭包
    auto()
}

// 显示闭包
func1 (closure: closures);
print(list)

// 将表达式自动生成闭包
func2 (auto: list.append(8))
print(list)

func serve(customer customerProvider: () -> String) {

    print(customerProvider())
}

serve { () -> String in
    return "没加@autoclosure"
}

func serve1(customer customerProvider: @autoclosure () -> String) {
    
    print(customerProvider())
}

serve1(customer: "加了@autoclosure")


func serve2(customer customerProvider: @autoclosure @escaping () -> String) {
    
    print(customerProvider())
}

serve1(customer: customersInLine.remove(at: 0))














